package com.example.codec;

import com.example.pojo.Header;
import com.example.pojo.NettyMessage;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>  </p>
 *
 * @author Chris
 * @since 2024/3/30 12:58
 */
public class NettyMessageDecoder extends LengthFieldBasedFrameDecoder {

    /**
     * 使用 slf4j 日志框架
     */
    private final static Logger log = LoggerFactory.getLogger(NettyMessageDecoder.class);

    MarshallingDecoder marshallingDecoder;

    public NettyMessageDecoder(int maxFrameLength, int lengthFieldOffset, int lengthFieldLength) throws IOException {
        // 指定标识消息长度的字段偏移量，以及消息长度自身所占的字节数，据此自动实现对拆包与粘包处理
        super(maxFrameLength, lengthFieldOffset, lengthFieldLength);

        this.marshallingDecoder = new MarshallingDecoder();
    }

    @Override
    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        final ByteBuf frame = (ByteBuf) super.decode(ctx, in);
        if (frame == null) {
            return null;
        }

        // 消息头
        final Header header = new Header();
        // 校验码（4 字节） = 0xABEF + MajorVer(1) + MinorVer(1)
        header.setCrcCode(frame.readInt());
        // 长度（4 字节） = 包括消息头和消息体
        header.setLength(frame.readInt());
        // 会话 ID（8 字节）
        header.setSessionID(frame.readLong());
        // 类型（1 字节）
        header.setType(frame.readByte());
        // 消息优先级（1 字节）
        header.setPriority(frame.readByte());

        // 可选字段（变长，用于扩展消息头）：Total_len len1 seg1 len2 seg2 ...
        final int size = frame.readInt();
        if (size > 0) {
            final Map<String, Object> attachment = new HashMap<>(size * 4 / 3);

            for (int i = 0; i < size; i++) {
                final byte[] keyArray = new byte[frame.readInt()];
                frame.readBytes(keyArray);
                attachment.put(new String(keyArray, StandardCharsets.UTF_8), this.marshallingDecoder.decode(frame));
            }

            header.setAttachment(attachment);
        }

        final NettyMessage message = new NettyMessage();

        if (frame.readableBytes() > 4) {
            message.setBody(this.marshallingDecoder.decode(frame));
        }

        message.setHeader(header);

        return message;
    }

}